Skip to content

feat: add cache() middleware for server-side response caching#3782

Open
bartlomieju wants to merge 1 commit intomainfrom
feat/cache-middleware
Open

feat: add cache() middleware for server-side response caching#3782
bartlomieju wants to merge 1 commit intomainfrom
feat/cache-middleware

Conversation

@bartlomieju
Copy link
Copy Markdown
Member

Summary

  • Adds a cache() middleware that uses the Web Cache API for server-side response caching
  • Routes opt in via standard Cache-Control response headers — no new config surface or framework-specific DSL needed
  • Supports stale-while-revalidate for ISR-like behavior (serve stale immediately, regenerate in background)
  • Skips caching for non-GET, private/no-store, Set-Cookie, non-200, and partial requests
  • Configurable via cacheName, methods, and custom shouldCache function
  • Follows existing middleware patterns (staticFiles(), cors(), etc.)

Usage

import { App, cache, staticFiles } from "fresh";

const app = new App()
  .use(staticFiles())
  .use(cache());

Routes opt in:

return page({ data }, {
  headers: { "Cache-Control": "public, max-age=60, stale-while-revalidate=300" },
});

Closes #8

Test plan

  • 13 unit tests covering: miss/hit, skip non-GET, skip private/no-store/Set-Cookie/non-200/partial, separate paths, expiry, stale-while-revalidate, custom shouldCache, custom methods
  • Integration testing with a real Fresh app

Adds a first-party middleware that caches responses using the Web Cache
API. Routes opt in via standard Cache-Control headers — no new config
surface needed. Supports stale-while-revalidate for ISR-like background
revalidation.

Closes #8
@bartlomieju
Copy link
Copy Markdown
Member Author

Nice work — the implementation is clean and follows the existing middleware conventions well. A few things that aren't covered here that might be worth considering as follow-ups:

Pluggable cache backend — This uses the Web Cache API exclusively, which works great on Deno Deploy but ties caching to the runtime. A storage option (or similar) that accepts a custom backend (Redis, in-memory LRU, etc.) would make this useful in self-hosted/Docker deployments where the Web Cache API may behave differently or not persist across restarts.

CDN cache coordination — For apps behind a CDN (Cloudflare, Fastly, etc.), it's common to need CDN-Cache-Control or Surrogate-Control headers that differ from the client-facing Cache-Control. The middleware could grow a hook for setting upstream cache headers independently, or at minimum the docs could note how to layer CDN headers alongside this middleware.

Vary header awareness — Right now the cache key is the request URL. If a route returns different content based on Accept, Accept-Language, or Accept-Encoding headers (common for i18n or content negotiation), the same URL would serve the wrong cached response. Respecting Vary in the cache key would prevent subtle bugs.

None of these block the initial merge — the current scope is solid and the SWR via queueMicrotask is a nice touch. Just flagging them as potential next steps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Caching of pages

1 participant